package com.piece.core.oauth.filter;

import com.google.common.collect.Multimap;
import com.piece.core.framework.constant.*;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.log.client.dto.UserDTO;
import com.piece.core.log.config.UserContextHolder;
import com.piece.core.web.authority.AuthorityFactory;
import com.piece.core.web.cookie.CookieUtil;
import com.piece.core.web.properties.SecurityProperties;
import com.piece.core.framework.support.dto.UserDetailDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Token验证过滤器
 */
@Slf4j
public class TokenFilter extends OncePerRequestFilter implements InitializingBean {

    // 存放所有可匿名访问的url
    private List<RequestMatcher> matchers = new ArrayList<>();
    private Multimap<HttpMethod, String> permits;

    private ApplicationContext applicationContext;
    private SecurityProperties securityProperties;
    private AuthorityFactory authorityFactory;
    private TokenStore tokenStore;

    public TokenFilter(ApplicationContext applicationContext, SecurityProperties securityProperties, AuthorityFactory authorityFactory, TokenStore tokenStore) {
        this.securityProperties = securityProperties;
        this.authorityFactory = authorityFactory;
        this.tokenStore = tokenStore;

        this.permits = authorityFactory.permitAllMethods(applicationContext, securityProperties);
        this.antMatchers(HttpMethod.GET.name(), permits.get(HttpMethod.GET).toArray(new String[0]));
        this.antMatchers(HttpMethod.POST.name(), permits.get(HttpMethod.POST).toArray(new String[0]));
        this.antMatchers(HttpMethod.PUT.name(), permits.get(HttpMethod.PUT).toArray(new String[0]));
        this.antMatchers(HttpMethod.DELETE.name(), permits.get(HttpMethod.DELETE).toArray(new String[0]));
    }

    /**
     * 初始化要拦截的url配置信息
     */
    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();
    }

    private void antMatchers(String method, String... antPatterns) {
        String[] var4 = antPatterns;
        int var5 = antPatterns.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            String pattern = var4[var6];
            matchers.add(new AntPathRequestMatcher(pattern, method));
        }
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        if (!matcher(request)) {
            try {
                String token = (null == authorityFactory.get()) ? null : authorityFactory.get().extractToken(request);
                if (StringUtil.isEmpty(token)) {
                    token = CookieUtil.getCookieValue(request, CookieConstants.SESSION_KEY);
                }

                if (StringUtil.isNotEmpty(token)) {
                    OAuth2Authentication authentication = this.tokenStore.readAuthentication(token);

                    if (null == authentication) {
                        if (this.isAuthenticated()) {
                            SecurityContextHolder.clearContext();
                        }

                        CookieUtil.deleteCookie(request, response, CookieConstants.SESSION_KEY);
                    }

                    Object details = authentication.getUserAuthentication().getDetails();
                    if (details instanceof UserDetailDTO) {
                        UserDetailDTO user = (UserDetailDTO) details;
                        UserContextHolder.set(new UserDTO(user.getId(), user.getUsername(), user.getAvatar(), user.permission(), user.getDeptId(), user.getDeptName()));
                    }

                    SecurityContextHolder.getContext().setAuthentication(authentication);
                    CookieUtil.setCookie(request, response, CookieConstants.SESSION_KEY, token,
                            Convert.toInt(TimeUnit.DAYS.toSeconds(securityProperties.getCookie().getExpire())));
                }
            } catch (Exception e) {
                // log.error("请求[{}] 验证token异常：{}", request.getServletPath(), e.getMessage());
            }
        }
        chain.doFilter(request, response);
    }

    private boolean matcher(HttpServletRequest request) {
        for (RequestMatcher matcher : matchers) {
            if (matcher.matches(request)) {
                return true;
            }
        }

        return false;
    }

    private boolean isAuthenticated() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return null != authentication && !(authentication instanceof AnonymousAuthenticationToken);
    }
}
